Class AudioClip

A single decoded audio clip that can send its signal to a Channel or a Master, be played, looped, analyzed, and controlled over time.

Example

import { AudioClip } from "@fluex/fluexgl-dsp";

const clip = new AudioClip(data);
clip.send(myChannel);

// Basic settings
clip.setVolume(0.8);
clip.setPanLevel(-0.2);
clip.setLoop(true);

// Start playback
clip.play();

// Manually seek to 30 seconds
clip.seek(30);

Constructor

Constructs a new AudioClip from pre-decoded source data.

new AudioClip(data: AudioSourceData): AudioClip;

Arguments


Properties

id: string

A unique id, automatically generated when constructing a new audio clip. Should NOT be changed.

label: string | null

A custom label. Can be changed.

loop: boolean

Whether the clip is set to loop when played.

isPlaying: boolean

Indicates whether this clip is currently playing.

startTime: number

The AudioContext.currentTime at which the current playback started.

offsetAtStart: number

The offset (in seconds) inside the buffer from which playback started.

playbackRate: number

The current playback rate applied to buffer sources. Defaults to 1. Updated automatically by setPitch().

pitch: number

The current pitch offset in semitones. Defaults to 0. Updated by setPitch()/resetPitch().

minPitchSemitones: number

The minimum allowed pitch, in semitones, accepted by setPitch(). Defaults to -24.

maxPitchSemitones: number

The maximum allowed pitch, in semitones, accepted by setPitch(). Defaults to 24.

progressUpdateSpeed: number

The interval in milliseconds used to track the audio clip's time progress. Default value is 20. The progress timer only runs while the clip is playing and at least one "progress" listener is registered, and stops when the last playback has ended.

gainNode: GainNode | null

Per-clip gain node used to control volume.

stereoPannerNode: StereoPannerNode | null

Per-clip stereo panner node used to control pan level.

context: AudioContext | null

Audio context, usually inherited from the DSP's pipeline context.

audioClipPlayer: AudioClipPlayer | null

AudioClipPlayer where this audio clip is attached to.


Methods

initialize(audioClipPlayer: AudioClipPlayer): void

Initializes the audio clip using the AudioClipPlayer's audio context. Usually not needed because send(channel: Channel | Master) initializes it automatically.

Arguments

Returns

  • void

play(timestamp?: number, offset?: number): AudioClip | null

Starts playback of the clip. The audio clip starts from the beginning if no arguments are provided.

Arguments

  • timestamp?: number - Absolute AudioContext.currentTime at which to start. If omitted, playback starts immediately. This argument is optional.
  • offset?: number - Offset in seconds inside the buffer to start from. If omitted, uses the current offsetAtStart.

Returns

  • AudioClip - The same AudioClip. Can be used to stack methods.
  • null - Returns null if this method failed, or if the maximum number of concurrent buffer source nodes (maxAudioBufferSourceNodes) has already been reached.

seek(seconds: number): AudioClip | void

Seeks to a given position (in seconds) inside the clip, clamped between 0 and duration. If the clip is currently playing, playback is stopped and restarted from the new position; otherwise offsetAtStart is updated for the next play() call.

Arguments

  • seconds: number - The position, in seconds, to seek to.

Returns

  • AudioClip - The same AudioClip. Can be used to stack methods.

stop(): AudioClip | null

Stops playback of this clip and disconnects all active buffer sources.

Arguments

No arguments

Returns

  • AudioClip - The same AudioClip. Can be used to stack methods.
  • null - Returns null if this method failed.

setVolume(volume: number): AudioClip

Sets the clip volume using its GainNode.

Arguments

  • volume: number - The desired gain value (linear)

Returns

  • AudioClip - The same AudioClip. Can be used to stack methods.

setPanLevel(panLevel: number): AudioClip

Sets the stereo pan level of the clip. Must be between -1 and 1.

Arguments

  • panLevel: number - Pan value between -1 (full left) and 1 (full right).

Returns

  • AudioClip - The same AudioClip. Can be used to stack methods.

setLoop(loop?: boolean): AudioClip

Enables or disables looping of this clip.

Arguments

  • loop?: boolean - When omitted, defaults to true.

Returns

  • AudioClip - The same AudioClip. Can be used to stack methods.

setMaxAudioBufferSourceNodes(value: number): AudioClip

Sets the maximum number of buffer source nodes (default 1). This can only be changed if the overrideMaxAudioBufferNodes property on DSP is set to true; otherwise a warning is logged and the value is left unchanged.

Arguments

  • value: number - The desired maximum amount of buffer source nodes.

Returns

  • AudioClip - The same AudioClip. Can be used to stack methods.

disconnectAllAudioBufferSourceNodes(): boolean

Stops and disconnects every currently active buffer source node for this clip.

Arguments

No arguments

Returns

  • boolean - true if the operation ran, false if this clip has no context.

setPitch(semitones: number): AudioClip

Sets the pitch of the clip in semitones, recalculating and applying the equivalent playbackRate (2 ^ (semitones / 12)) to all active buffer sources.

Arguments

  • semitones: number - The desired pitch offset in semitones. Must be between minPitchSemitones and maxPitchSemitones.

Returns

  • AudioClip - The same AudioClip. Can be used to stack methods.

resetPitch(): AudioClip

Resets the pitch back to 0 semitones. Shorthand for setPitch(0).

Arguments

No arguments

Returns

  • AudioClip - The same AudioClip. Can be used to stack methods.

setPlaybackRateInSemitones(semitones: number): AudioClip

Deprecated. Use setPitch() instead.

Arguments

  • semitones: number - The desired pitch offset in semitones.

Returns

  • AudioClip - The same AudioClip. Can be used to stack methods.

getChannelData(channel?: number): Float32Array

Returns the raw PCM sample data for a single channel of the underlying AudioBuffer.

Arguments

  • channel?: number - Zero-based channel index. Defaults to 0.

Returns

  • Float32Array

addEventListener<K extends keyof AudioClipEventMap>(event: K, cb: AudioClipEventMap[K]): () => void

Registers a listener for clip events.

Arguments

  • event: K (keyof AudioClipEventMap) - Event name (e.g. "progress").
  • cb: (event: (event from keyof AudioClipEventMap)) => void - Callback function.

Returns

  • () => void - Unsubscribe function to remove the listener.

once<K extends keyof AudioClipEventMap>(event: K, cb: AudioClipEventMap[K]): () => void

Registers a one-time event listener that automatically removes itself after the first call.

Arguments

  • event: K (keyof AudioClipEventMap) - Event name (e.g. "progress").
  • cb: (event: (event from keyof AudioClipEventMap)) => void - Callback function.

Returns

  • () => void - Unsubscribe function to remove the listener.

removeEventListener<K extends keyof AudioClipEventMap>(event: K, cb: AudioClipEventMap[K]): AudioClip

Removes a specific listener from an event.

Arguments

  • event: K (keyof AudioClipEventMap) - Event name (e.g. "progress").
  • cb: (event: (event from keyof AudioClipEventMap)) => void - Callback function.

Returns

  • AudioClip - The same instance, for chaining.

clearEventListeners(event?: keyof AudioClipEventMap): AudioClip

Clears event listeners.

Arguments

  • event?: keyof AudioClipEventMap - If provided, clears listeners only for that event.

Returns

  • AudioClip - The same instance, for chaining

send(channel: Channel | Master): void

Attaches this audio clip to a Channel or a Master and sends its signal to the channel.

Arguments

  • channel: Channel | Master - The channel where this audio clip's signal should be sent.

Returns

  • void

unsend(channel: Channel | Master): void

Detaches this audio clip from a Channel or a Master and stops sending this clip's signal to the channel.

Arguments

  • channel: Channel | Master - The channel that should stop receiving this clip's signal.

Returns

  • void

detachFromAudioClipPlayer(audioClipPlayer: AudioClipPlayer): void

Detaches this clip from a specific AudioClipPlayer. If that player was the clip's active player, the next remaining player (if any) becomes active. If no players remain, the clip is stopped.

Arguments

Returns

  • void

Events

"progress"

Emitted periodically while the clip is playing, on an interval controlled by progressUpdateSpeed. Payload type (AudioClipOnProgressEvent):

type ProgressPayload = {
    current: number;          // Current playback position in seconds
    startTime: number;        // AudioContext timestamp when playback started
    offset: number;           // Offset in seconds at which playback started
    contextTimestamp: number; // Current AudioContext.currentTime
    formatted: string;        // Human readable time, e.g. "01:23"
};

Registered via

clip.addEventListener("progress", (event) => {
    console.log(event.current, event.formatted);
});

"initialize"

Emitted once, right after initialize() finishes wiring up this clip's audio nodes. Payload (simplified):

type InitializePayload = {
    durationOfInitialization: number; // Time in ms the initialization took
    context: AudioContext | null;
};

"play"

Emitted every time play() successfully starts a new buffer source. Payload (simplified):

type PlayPayload = {
    timestamp: number;                            // Date.now() at play time
    audioBufferSourceNodes: AudioBufferSourceNode[]; // All currently active buffer sources
    context: AudioContext;
};

Getters and setters

get currentPlaybackTime(): number

Returns the current playback time in seconds relative to the start of the buffer. Returns 0 when the clip is not playing or no audio context is available. Otherwise: offsetAtStart + (audioContext.currentTime - startTime).

get duration(): number

Total duration of the underlying audio buffer in seconds.

get volume(): number

Current gain value read from gainNode. Returns 0 when no gain node is available.

get stereoPanning(): number

Current pan value read from stereoPannerNode. Returns 1 when no stereo panner node is available.

get formattedDuration(): string

Duration formatted as "mm:ss".

get sampleRate(): number

Sample rate of the underlying AudioBuffer.

get numberOfChannels(): number

Number of channels in the underlying AudioBuffer.

get byteLength(): number

Byte length of the original ArrayBuffer used to construct this clip.


Examples

Example 1: playing a simple background music

import { DspPipeline, loadAudioSource, AudioClip } from "@fluex/fluexgl-dsp";

(async function() {
    const pipeline = new DspPipeline({
        pathToWasm: "/data/fluexgl-dsp-wasm_bg.wasm",
        pathToWorklet: "/data/fluexgl-dsp-processor.worklet"
    });

    await pipeline.initializeDpsPipeline();
    
    const audioDevice = await pipeline.resolveDefaultAudioOutputDevice();
    if(!audioDevice) return;

    const audioSource = await loadAudioSource("/music.mp3");
    if(!audioSource) return;

    const audioClip = new AudioClip(audioSource);
    const myChannel = audioDevice.createChannel();

    audioClip.send(myChannel);
    myChannel.send(audioDevice.getMasterChannel());

    window.addEventListener("mousedown", function() {
        audioClip.play();
    });
})();

Example 2: mimicking the sound of a machine gun

import { DspPipeline, loadAudioSource, AudioClip } from "@fluex/fluexgl-dsp";

(async function() {
    const pipeline = new DspPipeline({
        pathToWasm: "/data/fluexgl-dsp-wasm_bg.wasm",
        pathToWorklet: "/data/fluexgl-dsp-processor.worklet",
        options: {
            overrideMaxAudioBufferNodes: true
        }
    });

    await pipeline.initializeDpsPipeline();
    
    const audioDevice = await pipeline.resolveDefaultAudioOutputDevice();
    if(!audioDevice) return;

    const audioSource = await loadAudioSource("/single-gun-shot.mp3");
    if(!audioSource) return;

    const audioClip = new AudioClip(audioSource);
    const myChannel = audioDevice.createChannel();

    audioClip.setMaxAudioBufferSourceNodes(200);
    audioClip.send(myChannel);
    myChannel.send(audioDevice.getMasterChannel());

    let lastTimestamp = Date.now();
    let isShooting = false;
    let shootDelayInMs = 100;

    function loop() {
        const now = Date.now();

        if((now - lastTimestamp >= shootDelayInMs) && isShooting) {
            audioClip.play();
            lastTimestamp = now;
        }

        return window.requestAnimationFrame(loop);
    }

    window.addEventListener("mousedown", function() { isShooting = true; });
    window.addEventListener("mouseup", function() { isShooting = false; });

    loop();
})();